// This code is derived from jcifs smb client library <jcifs at samba dot org>
// Ported by J. Arturo <webmaster at komodosoft dot net>
//  
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
// 
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
// 
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
using System;
using System.Text;
using SharpCifs.Util;
using SharpCifs.Util.Sharpen;

namespace SharpCifs.Netbios
{
    public class Name
    {
        private const int TypeOffset = 31;

        private const int ScopeOffset = 33;

        private static readonly string DefaultScope
            = Config.GetProperty("jcifs.netbios.scope");

        internal static readonly string OemEncoding
            = Config.GetProperty("jcifs.encoding", Runtime.GetProperty("file.encoding"));

        public string name;

        public string Scope;

        public int HexCode;

        internal int SrcHashCode;

        public Name()
        {
        }

        public Name(string name, int hexCode, string scope)
        {
            if (name.Length > 15)
            {
                name = Runtime.Substring(name, 0, 15);
            }
            this.name = name.ToUpper();
            this.HexCode = hexCode;
            this.Scope = !string.IsNullOrEmpty(scope) ? scope : DefaultScope;
            SrcHashCode = 0;
        }

        internal virtual int WriteWireFormat(byte[] dst, int dstIndex)
        {
            // write 0x20 in first byte
            dst[dstIndex] = unchecked(0x20);
            // write name
            try
            {
                byte[] tmp = Runtime.GetBytesForString(name, OemEncoding
                    );
                int i;
                for (i = 0; i < tmp.Length; i++)
                {
                    dst[dstIndex + (2 * i + 1)]
                        = unchecked((byte)(((tmp[i] & unchecked(0xF0)) >> 4) + unchecked(0x41)));
                    dst[dstIndex + (2 * i + 2)]
                        = unchecked((byte)((tmp[i] & unchecked(0x0F)) + unchecked(0x41)));
                }
                for (; i < 15; i++)
                {
                    dst[dstIndex + (2 * i + 1)] = unchecked(unchecked(0x43));
                    dst[dstIndex + (2 * i + 2)] = unchecked(unchecked(0x41));
                }
                dst[dstIndex + TypeOffset]
                    = unchecked((byte)(((HexCode & unchecked(0xF0)) >> 4) + unchecked(0x41)));
                dst[dstIndex + TypeOffset + 1]
                    = unchecked((byte)((HexCode & unchecked(0x0F)) + unchecked(0x41)));
            }
            catch (UnsupportedEncodingException)
            {
            }
            return ScopeOffset + WriteScopeWireFormat(dst, dstIndex + ScopeOffset);
        }

        internal virtual int ReadWireFormat(byte[] src, int srcIndex)
        {
            byte[] tmp = new byte[ScopeOffset];
            int length = 15;
            for (int i = 0; i < 15; i++)
            {
                tmp[i] = unchecked(
                    (byte)(
                        ((src[srcIndex + (2 * i + 1)] & unchecked(0xFF)) - unchecked(0x41)) << 4
                    )
                );
                tmp[i] |= unchecked(
                    (byte)(
                        ((src[srcIndex + (2 * i + 2)] & unchecked(0xFF)) - unchecked(0x41))
                            & unchecked(0x0F)
                    )
                );
                if (tmp[i] != unchecked((byte)' '))
                {
                    length = i + 1;
                }
            }
            try
            {
                name = Runtime.GetStringForBytes(tmp, 0, length, OemEncoding
                    );
            }
            catch (UnsupportedEncodingException)
            {
            }
            HexCode = ((src[srcIndex + TypeOffset] & unchecked(0xFF)) - unchecked(0x41)) << 4;
            HexCode |= ((src[srcIndex + TypeOffset + 1] & unchecked(0xFF)) - unchecked(0x41))
                        & unchecked(0x0F);
            return ScopeOffset + ReadScopeWireFormat(src, srcIndex + ScopeOffset);
        }

        internal int ReadWireFormatDos(byte[] src, int srcIndex)
        {

            int length = 15;
            byte[] tmp = new byte[length];

            Array.Copy(src, srcIndex, tmp, 0, length);

            try
            {
                name = Runtime.GetStringForBytes(tmp, 0, length).Trim();
            }
            catch (Exception)
            {
            }

            HexCode = src[srcIndex + length];

            return length + 1;
        }


        internal virtual int WriteScopeWireFormat(byte[] dst, int dstIndex)
        {
            if (Scope == null)
            {
                dst[dstIndex] = unchecked(unchecked(0x00));
                return 1;
            }
            // copy new scope in
            dst[dstIndex++] = unchecked((byte)('.'));
            try
            {
                Array.Copy(Runtime.GetBytesForString(Scope, OemEncoding), 
                           0, dst, dstIndex, Scope.Length);
            }
            catch (UnsupportedEncodingException)
            {
            }
            dstIndex += Scope.Length;
            dst[dstIndex++] = unchecked(unchecked(0x00));
            // now go over scope backwards converting '.' to label length
            int i = dstIndex - 2;
            int e = i - Scope.Length;
            int c = 0;
            do
            {
                if (dst[i] == '.')
                {
                    dst[i] = unchecked((byte)c);
                    c = 0;
                }
                else
                {
                    c++;
                }
            }
            while (i-- > e);
            return Scope.Length + 2;
        }

        internal virtual int ReadScopeWireFormat(byte[] src, int srcIndex)
        {
            int start = srcIndex;
            int n;
            StringBuilder sb;
            if ((n = src[srcIndex++] & unchecked(0xFF)) == 0)
            {
                Scope = null;
                return 1;
            }
            try
            {
                sb = new StringBuilder(Runtime.GetStringForBytes(src, srcIndex, n, OemEncoding));
                srcIndex += n;
                while ((n = src[srcIndex++] & unchecked(0xFF)) != 0)
                {
                    sb.Append('.').Append(Runtime.GetStringForBytes(src, srcIndex, n, OemEncoding));
                    srcIndex += n;
                }
                Scope = sb.ToString();
            }
            catch (UnsupportedEncodingException)
            {
            }
            return srcIndex - start;
        }

        public override int GetHashCode()
        {
            int result;
            result = name.GetHashCode();
            result += 65599 * HexCode;
            result += 65599 * SrcHashCode;
            if (Scope != null && Scope.Length != 0)
            {
                result += Scope.GetHashCode();
            }
            return result;
        }

        public override bool Equals(object obj)
        {
            Name n;
            if (!(obj is Name))
            {
                return false;
            }
            n = (Name)obj;
            if (Scope == null && n.Scope == null)
            {
                return name.Equals(n.name) && HexCode == n.HexCode;
            }
            return name.Equals(n.name) && HexCode == n.HexCode && Scope.Equals(n.Scope);
        }

        public override string ToString()
        {
            StringBuilder sb = new StringBuilder();

            //return "";

            string n = name;
            // fix MSBROWSE name
            if (n == null)
            {
                n = "null";
            }
            else
            {
                if (n[0] == unchecked(0x01))
                {
                    char[] c = n.ToCharArray();
                    c[0] = '.';
                    c[1] = '.';
                    c[14] = '.';
                    n = new string(c);
                }
            }
            sb.Append(n).Append("<").Append(Hexdump.ToHexString(HexCode, 2)).Append(">");
            if (Scope != null)
            {
                sb.Append(".").Append(Scope);
            }
            return sb.ToString();
        }
    }
}
